package com.gitee.ed.pattern.proxy;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.TypeUtil;
import com.gitee.ed.common.CommonUtils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.*;

/**
 * 抽象代理处理器
 * <p>
 * 该处理器子类通过泛型 <Target> {@link Target} 指定其代理目标接口类型
 * 并通过 doProxy {@link ProxyHandler#invoke(Invocation)} 方法代理原接口中的方法
 *
 * @author hongda.li
 */
public abstract class ProxyHandler<Target> implements InvocationHandler {
    /**
     * equals 方法名称
     */
    protected static final String METHOD_EQUALS = "equals";
    /**
     * toString 方法名称
     */
    protected static final String METHOD_TO_STRING = "toString";
    /**
     * hashCode 方法名称
     */
    protected static final String METHOD_HASH_CODE = "hashCode";
    /**
     * 代理目标类型
     * 该类型必须属于接口类型
     */
    private final Class<Target> target;
    /**
     * 代理目标接口的实现类
     */
    private final Target targetInstance;
    /**
     * 方法映射关系，避免运行时的反射获取
     */
    private final Map<String, Method> methodMap;
    /**
     * 拦截链
     */
    private List<ProxyInterceptor<Target>> interceptorList;

    protected ProxyHandler() {
        this(null);
    }

    @SuppressWarnings("unchecked")
    protected ProxyHandler(Target targetInstance) {
        this.target = (Class<Target>) TypeUtil.getTypeArgument(this.getClass());
        Assert.isTrue(this.target != null && this.target.isInterface(), "The Jdk dynamic proxy target must be interface");
        this.targetInstance = targetInstance;
        if (targetInstance == null) {
            this.methodMap = Collections.emptyMap();
        } else {
            this.methodMap = CommonUtils.toLinkedHashMap(
                    Method::getName,
                    method -> ReflectUtil.getMethodByName(targetInstance.getClass(), method.getName()),
                    ReflectUtil.getMethods(target)
            );
        }
    }

    /**
     * 添加拦截器
     *
     * @param interceptor 拦截器
     * @return 链式调用
     */
    public ProxyHandler<Target> addInterceptor(ProxyInterceptor<Target> interceptor) {
        if (interceptorList == null) {
            interceptorList = new ArrayList<>();
        }
        interceptorList.add(interceptor);
        return this;
    }

    /**
     * 批量添加拦截器
     *
     * @param interceptorList 拦截器集合
     * @return 链式调用
     */
    public ProxyHandler<Target> addInterceptorList(List<ProxyInterceptor<Target>> interceptorList) {
        Objects.requireNonNull(interceptorList);
        if (this.interceptorList == null) {
            this.interceptorList = new ArrayList<>();
        }
        this.interceptorList.addAll(interceptorList);
        return this;
    }

    /**
     * 根据指定排序器进行排序
     *
     * @param comparator 自定义拦截器排序逻辑
     * @return 链式调用
     */
    public ProxyHandler<Target> sort(Comparator<ProxyInterceptor<Target>> comparator) {
        if (interceptorList == null) {
            return this;
        }
        this.interceptorList.sort(comparator);
        return this;
    }

    /**
     * 根据拦截器的优先级注解进行排序 {@link com.gitee.ed.common.Priority}
     *
     * @return 链式调用
     */
    public ProxyHandler<Target> sort() {
        return this.sort(Comparator.comparingInt(interceptor -> CommonUtils.parsePriority(interceptor.getClass())));
    }

    /**
     * 获取代理目标类型
     *
     * @return 代理目标类型
     */
    public final Class<Target> getTarget() {
        return target;
    }

    /**
     * 获取代理目标类型的实现类
     *
     * @return 实现类
     */
    public Target getTargetInstance() {
        return targetInstance;
    }

    /**
     * 获取拦截器链
     *
     * @return 拦截器链
     */
    public List<ProxyInterceptor<Target>> getInterceptorList() {
        return interceptorList;
    }

    /**
     * 获取全部的方法映射
     *
     * @return 方法映射
     */
    public Map<String, Method> getMethodMap() {
        return methodMap;
    }

    /**
     * 通过模板模式进一步封装代理逻辑
     * equals、toString、hashCode 会默认由子类的 equals、toString、hashCode 代理实现
     * 若需覆盖这一行为，需要重写 proxyHashCode()、proxyToString()、proxyEquals() 方法
     * 其余方法由 doProxy() 实现，该方法也是子类必须实现的方法
     *
     * @param proxy  the proxy instance that the method was invoked on
     * @param method the {@code Method} instance corresponding to
     *               the interface method invoked on the proxy instance.  The declaring
     *               class of the {@code Method} object will be the interface that
     *               the method was declared in, which may be a superinterface of the
     *               proxy interface that the proxy class inherits the method through.
     * @param args   an array of objects containing the values of the
     *               arguments passed in the method invocation on the proxy instance,
     *               or {@code null} if interface method takes no arguments.
     *               Arguments of primitive types are wrapped in instances of the
     *               appropriate primitive wrapper class, such as
     *               {@code java.lang.Integer} or {@code java.lang.Boolean}.
     * @return 代理实现结果
     */
    @Override
    public final Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // targetInstance 为空时(仅代理接口)，会将 JDK 生成的代理类实例转为 Target 类型
        // targetInstance 不为空(代理实现了接口的类)，会将实现类转为 Target 类型
        Target actual = Optional.ofNullable(targetInstance).orElse(target.cast(proxy));

        // 根据方法名称获取对应的实现，此处默认实现 equals、hashCode、toString 方法
        return switch (method.getName()) {
            case METHOD_EQUALS -> this.proxyEquals(args[0]);
            case METHOD_TO_STRING -> this.proxyToString();
            case METHOD_HASH_CODE -> this.proxyHashCode();
            default -> this.invoke(new Invocation<>(target, actual, method, args));
        };
    }

    /**
     * 子类实现的真正代理逻辑
     * 重写该方法会导致原有的拦截器逻辑失效
     *
     * @param invocation 调用参数，包含代理目标类型、代理实例、代理方法、代理参数
     * @return 代理结果
     */
    protected Object invoke(Invocation<Target> invocation) throws Throwable {
        // 未配置拦截器，直接执行原方法
        if (this.interceptorList == null) {
            return this.invokeRaw(invocation);
        }

        // 过滤出需要拦截当前方法的拦截器
        final List<ProxyInterceptor<Target>> interceptors = this.interceptorList.stream()
                .filter(interceptor -> interceptor.doIntercept(invocation.getMethod()))
                .toList();

        try {
            // 前置拦截
            for (ProxyInterceptor<Target> interceptor : interceptors) {
                if (!interceptor.before(invocation)) {
                    return invocation.getResult();
                }
            }

            // 执行原方法
            invocation.setResult(this.invokeRaw(invocation));

            // 成功回调
            interceptors.forEach(interceptor -> interceptor.onSuccess(invocation));

            return invocation.getResult();
        } catch (Exception exception) {
            // 保存原异常
            invocation.setException(exception);

            // 失败回调
            for (ProxyInterceptor<Target> interceptor : interceptors) {
                interceptor.onFailure(invocation);
            }

            return invocation.getResult();
        } finally {

            // 完成回调
            interceptors.forEach(interceptor -> interceptor.onFinally(invocation));
        }
    }

    /**
     * 执行被代理对象的原方法
     * 被代理对象是接口时，此方法会直接返回空
     * 被代理对象是实例时，此方法会执行原方法
     *
     * @param invocation 调用参数
     * @return 执行结果
     */
    protected Object invokeRaw(Invocation<Target> invocation) throws Throwable {
        if (targetInstance == null && !invocation.getMethod().isDefault()) {
            // 仅代理接口时，因接口无具体实现，因此直接返回空值
            // 但有一种特殊情况，接口中定义了默认方法，此时会交由后续逻辑调用执行
            return null;
        } else {
            // 代理具体对象，调用对象的原方法，理论上方法不为空
            Method method = methodMap.get(invocation.getMethod().getName());
            try {
                return ReflectUtil.invokeRaw(invocation.getProxy(), method, invocation.getArgs());
            } catch (InvocationTargetException e) {
                throw Optional.ofNullable(e.getTargetException()).orElse(e);
            }
        }
    }

    /**
     * 代理 hashCode() 方法
     *
     * @return 子类默认的 hashCode() 方法
     */
    protected int proxyHashCode() {
        return this.hashCode();
    }

    /**
     * 代理 toString() 方法
     *
     * @return 子类默认的 toString() 方法
     */
    protected String proxyToString() {
        return this.toString();
    }

    /**
     * 代理 equals() 方法
     *
     * @return 子类默认的 equals() 方法
     */
    protected boolean proxyEquals(Object obj) {
        return this.equals(obj) || (obj != null && this.equals(Proxy.getInvocationHandler(obj)));
    }
}
